<template>
  <div class="w100vh h100vw bg-#fff">
    <div style="width: 100vw; height: 100vh" id="3D"></div>
  </div>
</template>
<script setup lang="ts">
import * as THREE from 'three'
import WebGL from 'three/addons/capabilities/WebGL.js'
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
const screenWidth = window.innerWidth
const screenHeight = window.innerHeight
onMounted(() => {
  // 是否支持 WebGL
  if (WebGL.isWebGL2Available()) {
    init3D()
    modelLoader()
    createSceneSky()
    createFloor()
    update()
  }
})
let composer: EffectComposer | null = null
let scene: THREE.Scene | null = null
const init3D = () => {
  const dom = document.getElementById('3D')
  if (!dom) return
  /* 创建场景 */
  scene = new THREE.Scene()
  // 雾化
  scene.fog = new THREE.FogExp2(0xcccccc, 0.001)
  scene.fog = new THREE.Fog('#d1e0ff', 500, 1200)
  /* 创建透视投影相机，视角75度，画幅比例 宽比高，近平面距离0.1，远平面1000 */
  const camera = new THREE.PerspectiveCamera(40, screenWidth / screenHeight, 0.1, 800)
  camera.position.set(20, 6, 10) // 设置相机位置
  camera.lookAt(0, 0, 0) // 设置相机方向（指向的场景对象）

  /* 创建渲染器 */
  const renderer = new THREE.WebGLRenderer({
    antialias: true, // 抗锯齿
    logarithmicDepthBuffer: true, // 深度贴图
    powerPreference: 'high-performance', // 渲染性能
  })
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.VSMShadowMap
  // 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
  renderer.setPixelRatio(window.devicePixelRatio)
  // //渲染器canvas宽高设为与窗口一致
  renderer.setSize(screenWidth, screenHeight)
  renderer.setClearAlpha(0.0) // 背景透明
  // 将渲染器对应的dom元素添加到body中
  dom.appendChild(renderer.domElement)
  // 创建后处理对象EffectComposer，WebGL渲染器作为参数
  composer = new EffectComposer(renderer)
  // 创建一个渲染器通道，场景和相机作为参数
  const renderPass = new RenderPass(scene, camera)
  // 设置renderPass通道
  composer.addPass(renderPass)

  /* 灯光 */
  createLight(scene)

  /* 辅助 */
  // 坐标
  const axesHelper = new THREE.AxesHelper(500)
  scene.add(axesHelper)
  // 帧率
  // const stats = new Stats();
  // document.body.appendChild(stats.domElement);

  // 设置相机控件轨道控制器OrbitControls
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.maxDistance = 550
  controls.maxPolarAngle = Math.PI / 2.2
  // controls.dampingFactor = 0.05; // 动态阻尼系数 就是鼠标拖拽旋转灵敏度

  /* gui */
  const gui = new GUI()
  //改变交互界面style属性
  gui.domElement.style.right = '0px'
  gui.domElement.style.width = '300px'
}

const lightgroup = ref<THREE.Group | null>(null)
const createLight = (scene: THREE.Scene) => {
  const ambient = new THREE.AmbientLight(0xffffff, 2)
  scene.add(ambient)
  const pointLight = new THREE.PointLight('#fff9f5', 1.0)
  pointLight.position.set(300, 300, 200) // 设置光源位置
  pointLight.intensity = 4 //光照强度
  pointLight.decay = 0.0 //设置光源不随距离衰减
  pointLight.castShadow = true
  pointLight.shadow.camera.near = 0.1
  pointLight.shadow.camera.far = 2000
  pointLight.shadow.mapSize.x = 2048 // 定义阴影贴图的宽度和高度,必须为2的整数此幂
  pointLight.shadow.mapSize.y = 2048 // 较高的值会以计算时间为代价提供更好的阴影质量
  pointLight.shadow.bias = -0.0005 //解决条纹阴影的出现
  pointLight.shadow.radius = 5 // 阴影边缘模糊度
  const pointLightHelper = new THREE.PointLightHelper(pointLight, 10)
  scene.add(pointLightHelper)
  const _lightgroup = new THREE.Group()
  _lightgroup.add(pointLight)
  // lightgroup.position.x = 80;
  scene.add(_lightgroup)
  lightgroup.value = _lightgroup
}

const modelLoader = () => {
  const loader = new GLTFLoader()
  // 创建GLTF加载器对象
  loader.load(import.meta.env.BASE_URL + 'Community.glb', function (gltf) {
    gltf.scene.traverse(function (obj) {
      // 通过使用 instanceof 进行类型检查，可以确保 obj 是 Mesh 类型，从而安全地访问 isMesh 属性。这种方法在 TypeScript 中是推荐的类型保护方式
      if (obj instanceof THREE.Mesh) {
        //判断是否是网格模型
        // console.log('模型节点', obj)
        // console.log('模型节点名字', obj.name.includes('glass'))
        obj.material = new THREE.MeshPhysicalMaterial({
          ...obj.material,
          transparent: true,
        })
        obj.receiveShadow = true
        obj.castShadow = true
        if (obj.name.includes('glass')) {
          obj.material = new THREE.MeshPhysicalMaterial({
            metalness: 0, //玻璃非金属
            roughness: 0, //玻璃表面光滑
            envMapIntensity: 1.0, //环境贴图对Mesh表面影响程度
            transmission: 1, //玻璃材质透光率，transmission替代opacity
            ior: 2, //折射率
          })
        }
      }
    })
    const model = gltf.scene
    model.scale.set(0.2, 0.2, 0.2)
    if (scene) {
      scene.add(model)
      const copyModel = model.clone()
      copyModel.position.x = 50
      scene.add(copyModel)
      const copyModel_1 = model.clone()
      copyModel_1.position.x = 0
      copyModel_1.position.z = 10
      scene.add(copyModel_1)
      const copyModel_2 = model.clone()
      copyModel_2.position.x = 10
      copyModel_2.position.z = 10
      scene.add(copyModel_2)
    }
  })
  // loader.load("/gilr.glb", function (gltf) {
  //   const model = gltf.scene;
  //   model.receiveShadow = true;
  //   model.castShadow = true;
  //   model.position.y = 10;
  //   that.scene.add(model);
  // });
}

const update = () => {
  //requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
  // this.stats.update();
  //执行渲染操作
  // this.renderer.render(this.scene, this.camera);
  if (composer) {
    composer.render()
  }
  //
  if (lightgroup.value) {
    lightgroup.value.rotation.y += 0.005
  }
  //
  // this.sceneBg.rotation.y += 0.0002
  // 更新动画
  requestAnimationFrame(update)
}

const createSceneSky = () => {
  const texLoader = new THREE.TextureLoader()
  // .load()方法加载图像，返回一个纹理对象Texture
  const texture = texLoader.load(import.meta.env.BASE_URL + 'envMap.jpg')
  texture.colorSpace = THREE.SRGBColorSpace //设置为SRGB颜色空间
  const geometry = new THREE.SphereGeometry(500)
  const material = new THREE.MeshStandardMaterial({
    color: '#fff',
    side: THREE.BackSide,
    map: texture,
    transparent: true,
  })
  const sphere = new THREE.Mesh(geometry, material)
  sphere.position.y = -25
  sphere.rotation.y = -35
  if (scene) {
    scene.add(sphere)
  }
}

const createFloor = () => {
  const texLoader = new THREE.TextureLoader()
  // .load()方法加载图像，返回一个纹理对象Texture
  const texture = texLoader.load(import.meta.env.BASE_URL + 'floor.jpeg')
  // 设置阵列模式
  texture.wrapS = THREE.RepeatWrapping
  texture.wrapT = THREE.RepeatWrapping
  // uv两个方向纹理重复数量
  texture.repeat.set(20, 20) //注意选择合适的阵列数量
  const geometry = new THREE.CircleGeometry(500)
  const material = new THREE.MeshLambertMaterial({
    color: '#3f3f3f',
    map: texture,
    side: THREE.DoubleSide,
  })
  const circle = new THREE.Mesh(geometry, material)
  circle.receiveShadow = true
  circle.castShadow = true
  circle.rotation.x = Math.PI / 2
  if (scene) {
    scene.add(circle)
  }
}
</script>
<style scoped lang="scss">
// .screen {
//   width: 100vw;
//   height: 100vh;
//   background-color: #ffffff;
// }
</style>
